import { observer, useLocalStore } from 'mobx-react';
import { FC, ReactNode, useCallback, useEffect, useRef } from 'react';
import _ from 'lodash';
import BigNumber from 'bignumber.js';

import draggerImg from '@assets/images/dragger.svg';
import { toNumber } from '@/utils/util';

export type DraggerWapperProps = {
  /** 挂载元素 */
  target?: (() => HTMLElement | Window | null) | undefined;

  /** 距离右侧位置 */
  right?: number | string;

  /** 距离顶部的位置 */
  top?: number | string;

  children: ReactNode;
};

/** 根据百分比计算实际数值 */
const getActNumber = (value?: number | string, totalNumber?: number): number => {
  if (!value) return 0;

  if (_.isNumber(value)) return value;

  if (_.isString(value) && totalNumber) {
    const _value = toNumber(value);
    return new BigNumber(Number(_value)).multipliedBy(totalNumber).toNumber();
  } else {
    return 0;
  }
};

const DraggerWapper: FC<DraggerWapperProps> = ({ target, children, right, top }) => {
  const store = useLocalStore(() => ({
    left: 0,
    top: 0,
    opacity: 1,
    filter: 0,
    scale: 1,
    xProportion: 0,
    yProportion: 0
  }));
  const dom = useRef<any>();

  useEffect(() => {
    // 设置初始位置
    handleResize(true);

    window.addEventListener('resize', () => handleResize(false));

    return () => window.removeEventListener('resize', () => handleResize(false));
  }, []);

  /** 获取可拖拽区域边界等属性 */
  const handleGetDraggerContainer = useCallback(() => {
    // 容器元素
    const draggerContainer = target
      ? target()
        ? dom.current?.parentNode
        : dom.current?.parentNode
      : (document.getElementsByTagName('body')[0] as any);
    const {
      right: containerRight = 0,
      bottom: containerBottom = 0,
      top: containerTop = 0,
      left: containerLeft = 0,
      width: containerWidth = 0,
      height: containerHeight = 0
    } = draggerContainer?.getBoundingClientRect() || {};

    return {
      containerWidth,
      containerHeight,
      containerTop,
      containerRight,
      containerBottom,
      containerLeft
    };
  }, [target, dom]);

  /** 获取拖拽元素边界等属性 */
  const handleGetDragger = useCallback(() => {
    // 实际拖拽元素
    const {
      right: draggerRight = 0,
      bottom: draggerBottom = 0,
      top: draggerTop = 0,
      left: draggerLeft = 0,
      width: draggerWidth = 0,
      height: draggerHeight = 0
    } = dom.current?.getBoundingClientRect() || {};

    return {
      draggerWidth,
      draggerHeight,
      draggerTop,
      draggerRight,
      draggerBottom,
      draggerLeft
    };
  }, [dom]);

  /**
   * 开始拖动
   * 记录拖动前元素的初始位置
   */
  const handleOnDragStart = useCallback((e: any) => {
    store.opacity = 0.1;
    store.filter = 100;
    store.scale = 0.7;
    const img = new Image();
    img.src = draggerImg;
    e.dataTransfer?.setDragImage(img, 0, 0);
  }, []);

  /**
   * 拖动中
   * 设置元素最新位置
   */
  const handleOnDrag = useCallback(
    (e: any) => {
      // 容器元素
      const {
        containerRight,
        containerBottom,
        containerTop,
        containerLeft
        // containerWidth,
        // containerHeight
      } = handleGetDraggerContainer();

      // 拖拽元素
      const { draggerWidth, draggerHeight } = handleGetDragger();

      // 元素移动后实际位置(不超出边界位置)
      let actX = e.clientX;
      let actY = e.clientY;

      // 超出左边界范围
      if (e.clientX <= containerLeft) {
        actX = containerLeft;
      }

      // 超出右边界范围
      if (e.clientX >= containerRight) {
        actX = containerRight - draggerWidth;
      }

      // 超出上边界范围
      if (e.clientY <= containerTop) {
        actY = containerTop;
      }

      // 超出下边界范围
      if (e.clientY >= containerBottom) {
        actY = containerBottom - draggerHeight;
      }

      // 更新位置和比例
      if (e.clientX) {
        store.left = actX;
        // store.xProportion = new BigNumber(actX).div(containerWidth).multipliedBy(100).toNumber();
      }
      if (e.clientY) {
        store.top = actY;
        // store.yProportion = new BigNumber(actY).div(containerHeight).multipliedBy(100).toNumber();
      }
      e.preventDefault();
    },
    [target]
  );

  /**
   * 拖动结束
   */
  const handleOnDragEnd = useCallback(() => {
    store.opacity = 1;
    store.filter = 0;
    store.scale = 1;
  }, []);

  /** 窗口缩放改变定位 */
  const handleResize = useCallback(
    (init = false) => {
      // 容器元素
      const {
        containerRight,
        containerBottom,
        containerTop,
        containerLeft,
        containerWidth,
        containerHeight
      } = handleGetDraggerContainer();

      // 拖拽元素
      const { draggerWidth, draggerHeight } = handleGetDragger();

      // 获取实际数值(百分比转化)
      let actLeft;
      let actTop;
      if (init) {
        const _left = getActNumber(right, containerWidth);
        const _top = getActNumber(top, containerHeight);
        // 设置初始比例
        store.xProportion = new BigNumber(_left).div(containerWidth).multipliedBy(100).toNumber();
        store.yProportion = new BigNumber(_top).div(containerHeight).multipliedBy(100).toNumber();

        actLeft = _left ? containerRight - draggerWidth - _left : containerLeft;
        actTop = _top ? containerBottom - draggerHeight - _top : containerTop;
      } else {
        const _left = getActNumber(`${store.xProportion}%`, containerWidth);
        const _top = getActNumber(`${store.yProportion}%`, containerHeight);
        actLeft = _left ? containerRight - draggerWidth - _left : containerLeft;
        actTop = _top ? containerBottom - draggerHeight - _top : containerTop;
      }

      // 设置位置
      store.left = actLeft;
      store.top = actTop;
    },
    [target, right, top]
  );

  return (
    <>
      <img src={draggerImg} style={{ display: 'none' }} />
      <div
        ref={dom}
        style={{
          position: 'fixed',
          zIndex: 1000,
          left: store.left,
          top: store.top,
          opacity: store.opacity,
          filter: `grayscale(${store.filter}%)`,
          transform: `scale(${store.scale})`
        }}
        draggable={true}
        onDragStart={handleOnDragStart}
        onDrag={handleOnDrag}
        onDragEnd={handleOnDragEnd}
      >
        {children}
      </div>
    </>
  );
};

export default observer(DraggerWapper);
